package scalab.core.component


import scala.collection.immutable.{Set, HashSet}
import scalab.core.gates.{Input, Output, Relay, MandatoryInput}

//note: blackbox does not outputs everything that it creates
//i.e. intermiediate build products are not yield!
trait BlackBox extends SinkComponent{

	import scalab.core.utils.AlgorithmFactory

	//do not change inherit order!
	override def mandatoryIn: Relay = {
		val self = this
		newRelay(new Relay with MandatoryInput /*with BlackBoxInput*/{
			override val component = self
		})
	}
	override def optionalIn: Relay = {
		val self = this
		newRelay(new Relay /*with BlackBoxInput*/{
			override val component = self
		})
	}
	private def newRelay(rel: Relay): Relay = {
		newInput(rel)
		rel
	}

	//@unchecked
	override def inputs: List[Relay] = super.inputs.asInstanceOf[List[Relay]]

	override val out: Relay = {
		val self = this
		new Relay with MandatoryInput {
			//blackbox box out needs to be fed by at least one task!
			//even if task does not yield any dependency (like acroread).
			override private[core] def lackMessage: String = "BlackBox's output needs source!"
			override val component = self
		}
	}

	/*private[core] override def update(fresh: Set[ExecutableComponent]): Pair[Boolean, Set[ExecutableComponent]] = 
		out.update(fresh)*/

	final private[core] def update(
		visited: Set[Component], 
		cycles: Set[(Output, Input)], 
		fresh: Set[ExecutableComponent]): (
			Boolean, 
			Set[Component], 
			Set[(Output, Input)], 
			Set[ExecutableComponent]
		) = {
			fireUpdateStarted
			val res = out.update(visited, cycles, fresh)
			fireUpdateFinished(res._1)
			res
		}

	final private[core] def upToDate(
		products: Set[Resource],
		visited: Set[Component], 
		cycles: Set[(Output, Input)], 
		fresh: Set[ExecutableComponent]): (
			Boolean, 
			Set[Component], 
			Set[(Output, Input)], 
			Set[ExecutableComponent]
		) = out.upToDate(products, visited, cycles, fresh)

	override def productDirs: List[String] = providers flatMap {p => p.productDirs}

	private[core] def updateId: String = out.updateId
	def instanceId = ""

	final private[core] def validate(
		visited: Set[Component], 
		cycles: Set[(Output, Input)], 
		validated: Set[SinkComponent]): (
			Boolean, 
			Set[Component], 
			Set[(Output, Input)], 
			Set[SinkComponent]
		) = {
			val (valid, moreVisited, moreCycles, moreValidated) = out.validate(visited, cycles, validated)
			if(valid)
				AlgorithmFactory.createValidate(this)(moreVisited, moreCycles, moreValidated)
			else
				(false, moreVisited, moreCycles, moreValidated)
		}

	BlackBox.allBoxes = BlackBox.allBoxes + this
}

object BlackBox{
	private[core] var allBoxes: Set[BlackBox] = HashSet.empty[BlackBox]
	def getAll: Set[BlackBox] = allBoxes
}
